using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Web;
using System.Xml.Linq;
using WebHelper.Helpers;

namespace WebHelper.Handlers
{
    [Serializable]
    public class BinaryDataHandler : IHttpHandler
    {
        #region Static Implementation

        private static Dictionary<string, BinaryDataHandler.Container> f_Items = new Dictionary<string, BinaryDataHandler.Container>();
        private static Dictionary<string, BinaryDataHandler.Container> Items
        {
            get { return BinaryDataHandler.f_Items; }
        }  

        public static string DefaultPath
        {
            get
            {
                return "BinaryData.axd";
            }
        }
        public static string ID
        {
            get
            {
                return "ID";
            }
        }

        public static string Register(bool writeToWebConfig, string filePath)
        {
            string path = BinaryDataHandler.DefaultPath;
            if (string.IsNullOrEmpty(filePath))
            {
                filePath = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
            }
            XDocument document = XDocument.Load(filePath);
            XElement httpHandlers = XmlHelper.CreateIfNotExist(document.Root, "system.web", "httpHandlers");
            var configElement = httpHandlers.Elements("add").FirstOrDefault(e => e.Attribute("type").Value.Contains(typeof(BinaryDataHandler).FullName));
            if (configElement != null)
            {
                path = configElement.Attribute("path").Value;
                writeToWebConfig = false;
            }
            XElement handlers = XmlHelper.CreateIfNotExist(document.Root, "system.webServer", "handlers");
            configElement = handlers.Elements("add").FirstOrDefault(e => e.Attribute("type") != null && e.Attribute("type").Value.Contains(typeof(BinaryDataHandler).FullName));
            if (configElement != null)
            {
                path = configElement.Attribute("path").Value;
                writeToWebConfig = false;
            }
            //iis 6
            configElement = httpHandlers.Elements("remove").FirstOrDefault(e => e.Attribute("name") != null && e.Attribute("name").Value.Contains(typeof(BinaryDataHandler).FullName));
            XElement element = new XElement("add",
                new XAttribute("path", path),
                new XAttribute("type", typeof(BinaryDataHandler).FullName + "," + typeof(BinaryDataHandler).Assembly.FullName),
                new XAttribute("verb", "*"),
                new XAttribute("validate", "false"));
            httpHandlers.Add(element);

            //trace
            Trace.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ") + "register in system.web", typeof(BinaryDataHandler).Name + "  ");

            //iis7
            configElement = handlers.Elements("remove").FirstOrDefault(e => e.Attribute("name") != null && e.Attribute("name").Value.Contains(typeof(BinaryDataHandler).FullName));
            if(configElement==null)
            {
                if (handlers.Elements("remove").Count() > 0)
                {
                    handlers.Elements("remove").Last().AddAfterSelf(new XElement("remove", new XAttribute("name", typeof(BinaryDataHandler).FullName)));
                }
                else
                {
                    handlers.AddFirst(new XElement("remove", new XAttribute("name", typeof(BinaryDataHandler).FullName)));
                }
            }
            element = new XElement("add",
                new XAttribute("name", typeof(BinaryDataHandler).FullName),
                new XAttribute("path", path),
                new XAttribute("type", typeof(BinaryDataHandler).FullName + "," + typeof(BinaryDataHandler).Assembly.FullName),
                new XAttribute("verb", "*"),
                new XAttribute("preCondition", "integratedMode"));
            handlers.Add(element);


            //trace
            Trace.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ") + "register in system.webServer", typeof(BinaryDataHandler).Name + "  ");


            if (writeToWebConfig)
            {
                document.Save(filePath);

                //trace
                Trace.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ") + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ") + "register and saved at" + filePath, typeof(BinaryDataHandler).Name + "  ");

            }
            return element.ToString();
        }
        public static void UnRegister(string filePath)
        {
            if (string.IsNullOrEmpty(filePath))
            {
                filePath = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
            }
            XDocument document = XDocument.Load(filePath);
            XElement configElement = null;
            XElement httpHandlers = XmlHelper.GetElementsOfPath(document.Root, "system.web", "httpHandlers").FirstOrDefault();
            if (httpHandlers != null)
            {
                configElement = httpHandlers.Elements("add").FirstOrDefault(e => e.Attribute("type").Value.Contains(typeof(BinaryDataHandler).FullName));
                if (configElement != null)
                {
                    configElement.Remove();
                }
            }
            XElement handlers = XmlHelper.GetElementsOfPath(document.Root, "system.webServer", "handlers").FirstOrDefault();
            if (handlers != null)
            {
                configElement = handlers.Elements("remove").FirstOrDefault(e => e.Attribute("name").Value.Contains(typeof(BinaryDataHandler).FullName));
                if (configElement != null)
                {
                    configElement.Remove();
                }
                configElement = handlers.Elements("add").FirstOrDefault(e => e.Attribute("type").Value.Contains(typeof(BinaryDataHandler).FullName));
                if (configElement != null)
                {
                    configElement.Remove();
                }
            }
            document.Save(filePath);

            //trace
            Trace.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ") + "unRegister from web.Congig", typeof(BinaryDataHandler).Name + "  ");

        }
        public static string Add(byte [] data,string responsContentType,bool removeAfterResponse)
        {
            Guid g = Guid.NewGuid();
            if (BinaryDataHandler.f_Items.Any(i => i.Value.Data == data && i.Value.ResponseContentType == responsContentType && i.Value.RemoveAfterResponse == removeAfterResponse))
            {
                g = new Guid(f_Items.First(i => i.Value.Data == data && i.Value.ResponseContentType == responsContentType && i.Value.RemoveAfterResponse == removeAfterResponse).Key);
            }
            else
            {
                BinaryDataHandler.f_Items.Add(g.ToString().ToLower(), new BinaryDataHandler.Container() { Data = data, RemoveAfterResponse = removeAfterResponse, ResponseContentType = responsContentType });
            }

            //trace
            Trace.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ") + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ") + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ") + "save data in dictionary", typeof(BinaryDataHandler).Name + "  ");

            string path = BinaryDataHandler.DefaultPath;
            XDocument document = XDocument.Load(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile);
            XElement httpHandlers = XmlHelper.CreateIfNotExist(document.Root, "system.web", "httpHandlers");
            var configElement = httpHandlers.Elements("add").FirstOrDefault(e => e.Attribute("type").Value.Contains(typeof(BinaryDataHandler).FullName));
            if (configElement != null)
            {
                path = configElement.Attribute("path").Value;
            }
            XElement handlers = XmlHelper.CreateIfNotExist(document.Root, "system.webServer", "handlers");
            configElement = handlers.Elements("add").FirstOrDefault(e => e.Attribute("type") != null && e.Attribute("type").Value.Contains(typeof(BinaryDataHandler).FullName));
            if (configElement != null)
            {
                path = configElement.Attribute("path").Value;
            }

            //trace
            Trace.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ") + "build path:" + path + "?" + BinaryDataHandler.ID + "=" + g.ToString(), typeof(BinaryDataHandler).Name + "  ");
            
            return path + "?" + BinaryDataHandler.ID + "=" + g.ToString();

        }
        #endregion

        public bool IsReusable
        {
            get { return true; }
        }
        public void ProcessRequest(HttpContext context)
        {
            string IDParam = context.Request.QueryString[BinaryDataHandler.ID];
            if (!string.IsNullOrEmpty(IDParam) && BinaryDataHandler.f_Items.ContainsKey(IDParam.ToLower()))
            {
                context.Response.ContentType = string.IsNullOrEmpty(BinaryDataHandler.f_Items[IDParam.ToLower()].ResponseContentType) ? "application/octet-stream" : BinaryDataHandler.f_Items[IDParam.ToLower()].ResponseContentType;
                context.Response.BinaryWrite(BinaryDataHandler.f_Items[IDParam.ToLower()].Data);

                //trace
                Trace.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ") + "write data to response", typeof(BinaryDataHandler).Name + "  ");

                if (BinaryDataHandler.f_Items[IDParam.ToLower()].RemoveAfterResponse)
                {
                    BinaryDataHandler.f_Items.Remove(IDParam.ToLower());

                    //trace
                    Trace.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ") + "remove data from memory", typeof(BinaryDataHandler).Name + "  ");

                }
            }
            else
            {
                context.Response.StatusCode = 404;

                //trace
                Trace.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ") + "no data found", typeof(BinaryDataHandler).Name + "  ");

            }
        }
        [Serializable]
        public class Container
        {
            public byte[] Data
            {
                get;
                set;
            }

            public bool RemoveAfterResponse
            {
                get;
                set;
            }

            public string ResponseContentType
            {
                get;
                set;
            }
        }
    }
}
